/*
 * Copyright (c) 2010, Nikolaus Moll
 * All rights reserved.
 */
package net.miblounge.gradecalculator.guicommon.models;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import javax.swing.event.EventListenerList;

import net.miblounge.gradecalculator.guicommon.interfaces.IMultiColumnTreeModelListener;
import net.miblounge.gradecalculator.guicommon.widgets.TreeIndex;

import com.trolltech.qt.gui.QBrush;
import com.trolltech.qt.gui.QFont;

public abstract class AbstractMultiColumnTreeModel {
	
	private final List<String> mimeTypes = new ArrayList<String>();
	
	private EventListenerList listeners = new EventListenerList();
	
	public abstract int getColumnCount();

	public abstract QFont getFont(final Object data);
	
	public abstract QBrush getBrush(final Object data);
	
	public abstract int getAlignment(final int column);
	
	public abstract int getRowCount(final TreeIndex index);
	
	public abstract List<String> getHeaderLabels();
	
	public abstract String getHeaderText(final int column);
	
	public abstract String getText(final Object data, final int column);

	public abstract void setText(final TreeIndex index, final int column, final String text);

	public abstract boolean isInlineEditorAllowed(final TreeIndex index, final int column);
	
	protected abstract <T> Object getChild(final T item, final int index);
	
	public final void addTreeEventListener(final IMultiColumnTreeModelListener listener) 
	{ 
		this.listeners.add(IMultiColumnTreeModelListener.class, listener);
	} 

	public final void removeTreeEventListener(final IMultiColumnTreeModelListener listener) 
	{ 
		this.listeners.remove(IMultiColumnTreeModelListener.class, listener);
	} 
	
	public boolean isRecursiveUpdateAllowed(final TreeIndex index)
	{
		return false;
	}

	public void recursiveUpdate(final TreeIndex row)
	{
		if ((row != null) && isRecursiveUpdateAllowed(row)) {
			notifyDataChanged(row.getParent());
		}
	}
	
	protected synchronized void notifyDataChanged(final TreeIndex row, final int column, final String newText) 
	{ 
		for (IMultiColumnTreeModelListener l : this.listeners.getListeners(IMultiColumnTreeModelListener.class)) {
			l.dataChangedEvent(row, column, newText);
			recursiveUpdate(row);
		}
	}

	public synchronized void notifyDataChanged(final TreeIndex row) 
	{ 
		for (IMultiColumnTreeModelListener l : this.listeners.getListeners(IMultiColumnTreeModelListener.class)) {
			l.dataChangedEvent(row);
			recursiveUpdate(row);
		}
	}
	protected synchronized void notifyChildInserted(final TreeIndex parent, final int row) 
	{ 
		for (IMultiColumnTreeModelListener l : this.listeners.getListeners(IMultiColumnTreeModelListener.class)) {
			l.childInserted(parent, row);
			notifyDataChanged(parent);
		}
	}
	
	protected synchronized void notifyChildRemoved(final TreeIndex parent, final int row) 
	{ 
		for (IMultiColumnTreeModelListener l : this.listeners.getListeners(IMultiColumnTreeModelListener.class)) {
			l.childRemoved(parent, row);
			notifyDataChanged(parent);
		}
	}
	
	protected void notifyChildMoved(final TreeIndex sourceIndex, final TreeIndex targetIndex) {
		for (IMultiColumnTreeModelListener l : this.listeners.getListeners(IMultiColumnTreeModelListener.class)) {
			l.childMoved(sourceIndex, targetIndex);
			notifyDataChanged(sourceIndex);
			notifyDataChanged(targetIndex);
		}
	}
	
	protected synchronized void notifySelectItem(final TreeIndex index) 
	{ 
		for (IMultiColumnTreeModelListener l : this.listeners.getListeners(IMultiColumnTreeModelListener.class)) {
			l.selectChild(index);
		}
	}
	
	public Object getRootData()
	{
		return null;
	}
	
	protected static Stack<Integer> createStack(final TreeIndex index)
	{
		final Stack<Integer> result = new Stack<Integer>();

		TreeIndex currentRow = index;
		while (currentRow != null) {
			result.push(currentRow.getRow());
			currentRow = currentRow.getParent();
		}
		
		return result;
	}
	
	public Object getData(final TreeIndex index) {
		if (index == null) {
			return getRootData();
		}
		
		final Stack<Integer> stack = createStack(index);
		
		Object result = null;
		while (!stack.isEmpty()) {
			result = getChild(result, stack.pop());
		}
		
		return result;
	}

	public boolean isDragEnabled()
	{
		return false;
	}

	public boolean isDropEnabled()
	{
		return false;
	}
	
	public boolean isDropIndicatorShown()
	{
		return false;
	}
	
	public final List<String> getMimeTypes()
	{
		return mimeTypes;
	}

	public boolean isDragAllowed(final TreeIndex index, final Object data) {
		return false;
	}

	public String getMime(final Class<?> aClass) {
		return "application/" + getMimeString() + ";value=\"" + aClass.getClass().toString() + "\"";
	}
	
	protected String getMimeString()
	{
		return "MultiColumnTree";
	}
	
	protected void registerMimeClass(final Class<?> aClass)
	{
		mimeTypes.add(getMime(aClass));
	}

	public String getDataString(final Object data) {
		if (data == null) {
			return "";
		}
		return data.toString();
	}

	public boolean isDropAllowed(final Object draggingObject, final TreeIndex index,
			final Object target) {
		return false;
	}

	public void dropItem(final TreeIndex sourceIndex, final Object draggingObject, final TreeIndex targetIndex, final Object target) {
	}
}
